package bnetwork;

import java.math.BigDecimal;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class Node {

	String name;
	int domain;
	Node[] deps;			//variables which this node depends on
	float[][] varprob;
	List<Node> kids;
	float[] postProb;

	//probability for
	//varprob[x][y] = the probability for this Node to be valued y, when the values of the other nodes are valued by x
	//when x is SIGMA{i}(deps[i].value*PI{j<i}(deps[j].domain))
	public Node(String name, int domain, Node[] deps, float[][] varprob){
		this.name = name;
		this.domain = domain;
		this.deps = deps;
		this.varprob = varprob;
		this.kids = new LinkedList<Node>();
	}

	public void addChild(Node kid){
		kids.add(kid);
	}

	private void normalize(float[] vals){
		float tmp = 0;
		for (int i=0; i<vals.length; i++){
			tmp = tmp + vals[i];
		}
		for (int i=0; i<vals.length; i++){ 
			vals[i] = round(vals[i]/tmp);
		}
	}

	public void enumerateAsk(Map<Node, Integer> evidence, Node start){
		postProb = new float[domain];

		Integer value = evidence.get(this);
		if (value==null){
			for (int i=0; i<domain; i++){
				Integer val = new Integer(i);
				evidence.put(this, val);
				postProb[i] = start.enumerateAll(evidence);
				evidence.remove(this);
			}
			normalize(postProb);
		} else {
			for (int i=0; i<domain; i++){
				postProb[i] = (value.intValue()==i ? 1 : 0);
			}
		}
	}

	public float enumerateAll(Map<Node, Integer> evidence){
		if (evidence.containsKey(this)){
			float post = 0;
			if (deps == null){
				post = varprob[0][evidence.get(this)];
			} else if (deps.length==1){
				post = varprob[evidence.get(deps[0])][evidence.get(this)];
			}
			for (Node child:kids){
				post = post*child.enumerateAll(evidence);
			}
			return post;
		} else {
			float ans = 0;
			for (int i=0; i<domain; i++){
				evidence.put(this, i);
				float post = 0;
				if (deps == null){
					post = varprob[0][i];
				} else if (deps.length==1){
					post = varprob[evidence.get(deps[0])][i];
				}				
				for (Node child:kids){
					post = post*child.enumerateAll(evidence);
				}
				evidence.remove(this);
				ans = ans + post;
			}
			return ans;
		}
	}

	private static float round(float d){
		int decimalPlace = 4;
		BigDecimal bd = new BigDecimal(Double.toString(d));
		bd = bd.setScale(decimalPlace,BigDecimal.ROUND_HALF_UP);
		return bd.floatValue();
	}

	public float getProb(int value){
		return postProb[value];
	}
}
